home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland Pascal with Objects 7.0 / DDEML.ZIP / DDEMLSRV.PAS < prev    next >
Pascal/Delphi Source File  |  1992-10-27  |  16KB  |  536 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Windows 3.1 DDEML Demonstration Program         }
  4. {   Copyright (c) 1992 by Borland International     }
  5. {                                                   }
  6. {***************************************************}
  7.  
  8. program DDEMLServer;
  9.  
  10. { This sample application uses the DDEML library in the server side of a
  11.   cooperative application.  This server is a simple data-entry application
  12.   which allows an operator to enter three data items, which are made
  13.   available through DDE to interested clients.
  14.  
  15.   This server makes its service available under the following names:
  16.  
  17.        Service: 'DataEntry'
  18.        Topic  : 'SampledData'
  19.        Items  : 'DataItem1', 'DataItem2', 'DataItem3'
  20.  
  21.   Conceivably, other topics under this service could be defined.  Things
  22.   such as historical data, information about the sampling, and so on
  23.   might make useful topics.
  24.  
  25.   You must run this server BEFORE running the client (DDEMLCLI.PAS), or
  26.   the client will fail the connection.
  27.  
  28.   The interface to this server is defined by the list of names (Service,
  29.   Topic, and Items) in the separate unit called DataEntry (DATAENTR.TPU).
  30.   The server makes the Items available in cf_Text format; they can be
  31.   converted and stored locally as integers by the client.
  32. }
  33.  
  34. uses Strings, WinTypes, WinProcs, OWindows, ODialogs, Win31, DDEML,
  35.   ShellAPI, BWCC, DataEntry;
  36.  
  37. {$R DDEMLSRV}
  38.  
  39. const
  40.  
  41. { Resource IDs }
  42.  
  43.   id_Menu    = 100;
  44.   id_About   = 100;
  45.   id_Icon    = 100;
  46.  
  47.   id_Value1  = 401;  { Used with the DataEntry Dialog }
  48.   id_Value2  = 402;
  49.   id_Value3  = 403;
  50.  
  51.   st_Message =   1;
  52.  
  53. { Menu command IDs }
  54.  
  55.   cm_DataEnter = 201;
  56.   cm_DataClear = 202;
  57.   cm_HelpAbout = 300;
  58.  
  59. type
  60.  
  61. { Application main window }
  62.  
  63.   PDDEServerWindow = ^TDDEServerWindow;
  64.   TDDEServerWindow = object(TWindow)
  65.     Inst       : Longint;
  66.     CallBack   : TCallback;
  67.     ServiceHSz : HSz;
  68.     TopicHSz   : HSz;
  69.     ItemHSz    : array [1..NumValues] of HSz;
  70.     ConvHdl    : HConv;
  71.     Advising   : array [1..NumValues] of Boolean;
  72.  
  73.     DataSample : TDataSample;
  74.  
  75.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  76.     destructor  Done; virtual;
  77.  
  78.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  79.     function  GetClassName: PChar; virtual;
  80.     procedure SetupWindow; virtual;
  81.     procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
  82.  
  83.     procedure CMDataEnter(var Msg: TMessage);
  84.       virtual cm_First + cm_DataEnter;
  85.     procedure CMDataClear(var Msg: TMessage);
  86.       virtual cm_First + cm_DataClear;
  87.     procedure CMHelpAbout(var Msg: TMessage);
  88.       virtual cm_First + cm_HelpAbout;
  89.  
  90.     function  MatchTopicAndService(Topic, Service: HSz): Boolean; virtual;
  91.     function  MatchTopicAndItem(Topic, Item: HSz): Integer; virtual;
  92.     function  WildConnect(Topic, Service: HSz;
  93.       ClipFmt: Word): HDDEData; virtual;
  94.     function  AcceptPoke(Item: HSz; ClipFmt: Word;
  95.       Data: HDDEData): Boolean; virtual;
  96.     function  DataRequested(TransType: Word; ItemNum: Integer;
  97.       ClipFmt: Word): HDDEData; virtual;
  98.   end;
  99.  
  100.  
  101. { Application object }
  102.  
  103.   TDDEServerApp = object(TApplication)
  104.     procedure InitMainWindow; virtual;
  105.   end;
  106.  
  107.  
  108. { Initialized globals }
  109.  
  110. const
  111.   DemoTitle   : PChar = 'DDEML Demo, Server Application';
  112.  
  113.   MaxAdvisories = 100;
  114.   NumAdvLoops : Integer = 0;
  115.  
  116.  
  117. { Global variables }
  118.  
  119. var
  120.   App: TDDEServerApp;
  121.  
  122.  
  123. { Local Function: CallBack Procedure for DDEML }
  124.  
  125. { This callback procedure responds to all transactions generated by the
  126.   DDEML.  The target Window object is obtained from the stored global,
  127.   and the appropriate methods within that objects are used to respond
  128.   to the given transaction, as indicated by the CallType parameter.
  129. }
  130. function CallbackProc(CallType, Fmt: Word; Conv: HConv; HSz1, HSz2: HSZ;
  131.   Data: HDDEData; Data1, Data2: Longint): HDDEData; export;
  132. var
  133.   ThisWindow: PDDEServerWindow;
  134.   ItemNum   : Integer;
  135. begin
  136.   CallbackProc := 0;    { See if proved otherwise }
  137.  
  138.   ThisWindow := PDDEServerWindow(App.MainWindow);
  139.  
  140.   case CallType of
  141.  
  142.     xtyp_WildConnect:
  143.       CallbackProc := ThisWindow^.WildConnect(HSz1, HSz2, Fmt);
  144.  
  145.     xtyp_Connect:
  146.       if Conv = 0 then
  147.       begin
  148.         if ThisWindow^.MatchTopicAndService(HSz1, HSz2) then
  149.           CallbackProc := 1;   { Connected! }
  150.       end;
  151. { When a connection is confirmed, record the conversation handle as the
  152.   window's own.
  153. }
  154.     xtyp_Connect_Confirm:
  155.       ThisWindow^.ConvHdl := Conv;
  156.  
  157. { The client has requested data, either as a direct request or
  158.   in response to an advisory.  Return the current state of the
  159.   data.
  160. }
  161.     xtyp_AdvReq, xtyp_Request:
  162.       begin
  163.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  164.         if ItemNum > 0 then
  165.           CallbackProc := ThisWindow^.DataRequested(CallType, ItemNum, Fmt);
  166.       end;
  167.  
  168. { Respond to Poke requests ... this demo only allows Pokes of DataItem1.
  169.   Return dde_FAck to acknowledge the receipt, 0 otherwise.
  170. }
  171.     xtyp_Poke:
  172.       begin
  173.         if ThisWindow^.AcceptPoke(HSz2, Fmt, Data) then
  174.           CallbackProc := dde_FAck;
  175.       end;
  176.  
  177. { The client has requested the start of an advisory loop.  Note
  178.   that we assume a "hot" loop.  Set the Advising flag to indicate
  179.   the open loop, which will be checked whenever the data is changed.
  180. }
  181.     xtyp_AdvStart:
  182.       begin
  183.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  184.         if ItemNum > 0 then
  185.         begin
  186.           if NumAdvLoops < MaxAdvisories then    { Arbitrary number }
  187.           begin
  188.             Inc(NumAdvLoops);
  189.             ThisWindow^.Advising[ItemNum] := True;
  190.             CallbackProc := 1;
  191.           end;
  192.         end;
  193.       end;
  194.  
  195. { The client has requested the advisory loop to terminate.
  196. }
  197.     xtyp_AdvStop:
  198.       begin
  199.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  200.         if ItemNum > 0 then
  201.         begin
  202.           if NumAdvLoops > 0 then
  203.           begin
  204.             Dec(NumAdvLoops);
  205.             if NumAdvLoops = 0 then
  206.               ThisWindow^.Advising[ItemNum] := False;
  207.             CallbackProc := 1;
  208.           end;
  209.         end;
  210.       end;
  211.   end;  { Case CallType }
  212.  
  213. end;
  214.  
  215.  
  216. { TDDEServerWindow Methods }
  217.  
  218. { Constructs an instance of the DDE Server Window.  Calls on the
  219.   inherited constructor, then sets up this objects own instandce
  220.   data.
  221. }
  222. constructor TDDEServerWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  223. var
  224.   I : Integer;
  225. begin
  226.   TWindow.Init(AParent, ATitle);
  227.  
  228.   Inst      := 0;      { Must be zero for first call to DdeInitialize }
  229.   @CallBack := nil;    { MakeProcInstance is called in SetupWindow    }
  230.  
  231.   for I := 1 to NumValues do
  232.   begin
  233.     DataSample[I]:= 0;
  234.     Advising[I]  := False;
  235.   end;
  236. end;
  237.  
  238. { Destroys an instance of the DDE Server Window.  Checks to see if the
  239.   Callback Proc Instance had been created, and frees it if so.  Also 
  240.   calls DdeUninitialize to terminate the conversation.  Then just calls
  241.   on the ancestral destructor to finish.
  242. }
  243. destructor TDDEServerWindow.Done;
  244. var
  245.   I : Integer;
  246. begin
  247.   if ServiceHSz <> 0 then
  248.     DdeFreeStringHandle(Inst, ServiceHSz);
  249.   if TopicHSz <> 0 then
  250.     DdeFreeStringHandle(Inst, TopicHSz);
  251.   for I := 1 to NumValues do
  252.     if ItemHSz[I] <> 0 then
  253.       DdeFreeStringHandle(Inst, ItemHSz[I]);
  254.  
  255.   if Inst <> 0 then
  256.     DdeUninitialize(Inst);   { Ignore the return value }
  257.     
  258.   if @CallBack <> nil then
  259.     FreeProcInstance(@CallBack);
  260.  
  261.   TWindow.Done;
  262. end;
  263.  
  264. { Redefines GetWindowClass to give this application its own Icon and
  265.   default menu.
  266. }
  267. procedure TDDEServerWindow.GetWindowClass(var AWndClass: TWndClass);
  268. begin
  269.   TWindow.GetWindowClass(AWndClass);
  270.   AWndClass.hIcon := LoadIcon(AWndClass.hInstance, PChar(id_Icon));
  271.   AWndClass.lpszMenuName := PChar(id_Menu);
  272. end;
  273.  
  274. { Returns the class name of this window.  This is necessary since we
  275.   redefine the inherited GetWindowClass method, above.
  276. }
  277. function TDDEServerWindow.GetClassName: PChar;
  278. begin
  279.   GetClassName := 'TDDEServerWindow';
  280. end;
  281.  
  282. { Completes the initialization of the DDE Server Window.  Initializes
  283.   the use of the DDEML by registering the services provided by this
  284.   application.  Recall that the actual names used to register are
  285.   defined in a separate unit (DataEntry), so that they can be used
  286.   by the client as well.
  287. }
  288. procedure TDDEServerWindow.SetupWindow;
  289. var
  290.   I : Integer;
  291. begin
  292.   TWindow.SetupWindow;
  293.  
  294.   @CallBack:= MakeProcInstance(@CallBackProc, HInstance);
  295.  
  296.   if DdeInitialize(Inst, CallBack, 0, 0) = dmlErr_No_Error then
  297.   begin
  298.     ServiceHSz:= DdeCreateStringHandle(Inst, DataEntryName, cp_WinAnsi);
  299.     TopicHSz  := DdeCreateStringHandle(Inst, DataTopicName, cp_WinAnsi);
  300.     for I := 1 to NumValues do
  301.       ItemHSz[I] := DdeCreateStringHandle(Inst, DataItemNames[I],
  302.         cp_WinAnsi);
  303.  
  304.     if DdeNameService(Inst, ServiceHSz, 0, dns_Register) = 0 then
  305.     begin
  306.       MessageBox(HWindow, 'Registration failed.', Application^.Name,
  307.         mb_IconStop);
  308.       PostQuitMessage(0);
  309.     end;
  310.   end
  311.   else
  312.     PostQuitMessage(0);
  313. end;
  314.  
  315. procedure TDDEServerWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
  316. type
  317.   TDataItem = record
  318.     Name: Pointer;
  319.     Value: Integer;
  320.   end;
  321.   TData = array[1..NumValues] of TDataItem;
  322. var
  323.   R: TRect;
  324.   S: array[0..255] of Char;
  325.   S1: array[0..512] of Char;
  326.   Len, I: Integer;
  327.   Data: TData;
  328. begin
  329.   GetClientRect(HWindow, R);
  330.   InflateRect(R, -10, 0);
  331.   LoadString(hInstance, st_Message, S, SizeOf(S));
  332.   for I := 1 to NumValues do
  333.   begin
  334.     Data[I].Name := DataItemNames[I];
  335.     Data[I].Value := DataSample[I];
  336.   end;
  337.   Len := wvsPrintf(S1, S, Data);
  338.   DrawText(PaintDC, S1, Len, R, dt_WordBreak);
  339. end;
  340.  
  341. { Returns True if the given Topic and Service match those supported
  342.   by this application.  False otherwise.
  343. }
  344. function TDDEServerWindow.MatchTopicAndService(Topic, Service: HSz): Boolean;
  345. begin
  346.   MatchTopicAndService := False;
  347.   if DdeCmpStringHandles(TopicHSz, Topic) = 0 then
  348.     if DdeCmpStringHandles(ServiceHSz, Service) = 0 then
  349.       MatchTopicAndService := True;
  350. end;
  351.  
  352. { Determines if the given Topic and Item match one supported by this
  353.   application.  Returns the Item Number of the supported item (in the
  354.   range 1..NumValues) if one is found, and zero if no match.
  355. }
  356. function TDDEServerWindow.MatchTopicAndItem(Topic, Item: HSz): Integer;
  357. var
  358.   I : Integer;
  359. begin
  360.   MatchTopicAndItem := 0;
  361.   if DdeCmpStringHandles(TopicHSz, Topic) = 0 then
  362.     for I := 1 to NumValues do
  363.       if DdeCmpStringHandles(ItemHSz[I], Item) = 0 then
  364.         MatchTopicAndItem := I;
  365. end;
  366.  
  367. { Responds to wildcard connect requests.  These requests are generated
  368.   whenever a client tries to connect to a server with either service or topic
  369.   name set to 0.  If a server detects a wild card match, it returns a
  370.   handle to an array of THSZPair's containing the matching supported Service
  371.   and Topic.
  372. }
  373. function TDDEServerWindow.WildConnect(Topic, Service: HSz;
  374.   ClipFmt: Word): HDDEData;
  375. var
  376.   TempPairs: array [0..1] of THSZPair;
  377.   Matched  : Boolean;
  378. begin
  379.   TempPairs[0].hszSvc  := ServiceHSz;
  380.   TempPairs[0].hszTopic:= TopicHSz;
  381.   TempPairs[1].hszSvc  := 0;     { 0-terminate the list }
  382.   TempPairs[1].hszTopic:= 0;
  383.  
  384.   Matched := False;
  385.  
  386.   if (Topic= 0) and (Service = 0) then
  387.     Matched := True                    { Complete wildcard }
  388.   else
  389.     if (Topic = 0) and (DdeCmpStringHandles(Service, ServiceHSz) = 0) then
  390.       Matched := True
  391.     else
  392.       if (DdeCmpStringHandles(Topic, TopicHSz) = 0) and (Service = 0) then
  393.         Matched := True;
  394.  
  395.   if Matched then
  396.     WildConnect := DdeCreateDataHandle(Inst, @TempPairs, SizeOf(TempPairs),
  397.       0, 0, ClipFmt, 0)
  398.   else
  399.     WildConnect := 0;
  400. end;
  401.  
  402. { Accepts and acts upon Poke requests from the Client.  For this
  403.   demonstration, allows only the value of DataItem1 to be changed by a Poke.
  404. }
  405. function TDDEServerWindow.AcceptPoke(Item: HSz; ClipFmt: Word;
  406.   Data: HDDEData): Boolean;
  407. var
  408.   DataStr   : TDataString;
  409.   Err       : Integer;
  410.   TempSample: TDataSample;
  411. begin
  412.   if (DdeCmpStringHandles(Item, ItemHSz[1]) = 0) and (ClipFmt = cf_Text) then
  413.   begin
  414.     DdeGetData(Data, @DataStr, SizeOf(DataStr), 0);
  415.     Val(DataStr, TempSample[1], Err);
  416.  
  417.     if TempSample[1] <> DataSample[1] then
  418.     begin
  419.       DataSample[1] := TempSample[1];
  420.       if Advising[1] then
  421.         DdePostAdvise(Inst, TopicHSz, ItemHSz[1]);
  422.     end;
  423.     InvalidateRect(HWindow, nil, True);
  424.     AcceptPoke := True;
  425.   end
  426.   else
  427.     AcceptPoke := False;
  428. end;
  429.  
  430. { Returns the data requested by the given TransType and ClipFmt values.
  431.   This could happen either in response to either an xtyp_Request or an
  432.   xtyp_AdvReq.  The ItemNum parameter indicates which of the supported
  433.   items (in the range 1..NumValues) was requested (note that this method
  434.   assumes that the caller has already established validity and ID of the
  435.   requested item using MatchTopicAndItem).  The corresponding data from
  436.   the DataSample instance variable is converted to text and returned.
  437. }
  438. function TDDEServerWindow.DataRequested(TransType: Word; ItemNum: Integer;
  439.   ClipFmt: Word): HDDEData;
  440. var
  441.   ItemStr: TDataString;   { Defined in DataEntry.TPU }
  442. begin
  443.   if ClipFmt = cf_Text then
  444.   begin
  445.     Str(DataSample[ItemNum], ItemStr);
  446.     DataRequested := DdeCreateDataHandle(Inst, @ItemStr, StrLen(ItemStr) + 1,
  447.       0, ItemHSz[ItemNum], ClipFmt, 0);
  448.   end
  449.   else
  450.     DataRequested := 0;
  451. end;
  452.  
  453. { Activates the data-entry dialog, and updates the stored
  454.   data when complete.
  455. }
  456. procedure TDDEServerWindow.CMDataEnter(var Msg: TMessage);
  457. const
  458.   ValEditIds : array [1..NumValues] of Integer = (id_Value1,
  459.     id_Value2, id_Value3);
  460. var
  461.   DataEntry  : PDialog;
  462.   Err, I     : Integer;
  463.   TempSample : TDataSample;
  464.   Ed         : PEdit;
  465.   TransferRec: array [1..NumValues] of record
  466.                                          ValStr : array [0..19] of Char;
  467.                                        end;
  468. begin
  469.   DataEntry := New(PDialog, Init(@Self, 'DATAENTRY'));
  470.  
  471.   for I := 1 to NumValues do
  472.   begin
  473.     Str(DataSample[I], TransferRec[I].ValStr);
  474.     New(Ed, InitResource(DataEntry, ValEditIds[I],
  475.       SizeOf(TransferRec[I].ValStr)));
  476.   end;
  477.  
  478.   DataEntry^.TransferBuffer := @TransferRec;
  479.  
  480.   if Application^.ExecDialog(DataEntry) = IdOK then
  481.   begin
  482.     for I := 1 to NumValues do
  483.     begin
  484.       Val(TransferRec[I].ValStr, TempSample[I], Err);
  485.  
  486.       if TempSample[I] <> DataSample[I] then
  487.       begin
  488.         DataSample[I] := TempSample[I];
  489.         if Advising[I] then
  490.           DdePostAdvise(Inst, TopicHSz, ItemHSz[I]);
  491.       end;
  492.     end;
  493.     InvalidateRect(HWindow, nil, True);
  494.   end;
  495. end;
  496.  
  497. { Clears the current data.
  498. }
  499. procedure TDDEServerWindow.CMDataClear(var Msg: TMessage);
  500. var
  501.   I : Integer;
  502. begin
  503.   for I := 1 to NumValues do
  504.   begin
  505.     DataSample[I] := 0;
  506.     if Advising[I] then
  507.       DdePostAdvise(Inst, TopicHSz, ItemHSz[I]);
  508.   end;
  509.   InvalidateRect(HWindow, nil, True);
  510. end;
  511.  
  512. { Posts the about box dialog for the DDE Server.
  513. }
  514. procedure TDDEServerWindow.CMHelpAbout(var Msg: TMessage);
  515. begin
  516.   Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
  517. end;
  518.  
  519.  
  520. { TDDEServerApp Methods }
  521.  
  522. procedure TDDEServerApp.InitMainWindow;
  523. begin
  524.   MainWindow := New(PDDEServerWindow, Init(nil, Application^.Name));
  525. end;
  526.  
  527.  
  528. { Main program }
  529.  
  530. begin
  531.   App.Init(DemoTitle);
  532.   App.Run;
  533.   App.Done;
  534. end.
  535.  
  536.